﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Triangle
{
    public class ConnectedComponent
    {
        private List<Node> collection;
        private readonly List<Node> visitedNodes = new List<Node>();
        public bool IsPure { get; private set; }
        public ConnectedComponent(IEnumerable<Node> stack)
        {
            collection = stack.ToList();
        }
        /// <summary>
        /// получает вершины прямоугольника
        /// </summary>
        /// <returns></returns>
        public List<Node> GetCorner()
        {
            var result = new List<Node>();
            collection = (from node in collection orderby node.I, node.J select node).ToList();
            /*Console.WriteLine("Sorted by X:");
            foreach (var node in collection)
                Console.WriteLine(node);*/
            result.Add(collection.First());
            result.Add(collection.Last());
            collection = (from node in collection orderby node.J descending, node.I select node).ToList();
            /*Console.WriteLine("Sorted by Y:");
            foreach (var node in collection)
                Console.WriteLine(node);*/
            result.Add(collection.First());
            result.Add(collection.Last());
            return result.Distinct().ToList();
        }
        /// <summary>
        /// проверяет можно ли построить треугольник по вершинам
        /// сначала проверка колличества вершин
        /// проверка перпиндикулярности векторов
        /// сортируем вершины
        /// проверка линии, т.е. есть ли необходимые точки для построения линии
        /// проверка что в компоненте нет лишних точек
        /// </summary>
        /// <param name="corners">набор вершин</param>
        /// <returns>площадь прямоугольника и 0 если не прямоугольник</returns>
        public double IsTriangle(List<Node> corners)
        {
            if (corners.Count != 4)
                return 0;
            var vectors = new List<Vector>(); // 12 векторов из каждой вершины в каждую
            var rectVectors = new List<Vector>(); // 8 перпендикулярных векторов
            for (int i = 0; i < 4; ++i)
            {
                for (int j = 0; j < 4; ++j)
                {
                    if (i == j) continue;
                    vectors.Add(new Vector(corners[i], corners[j]));
                }
                var newVectors = Vector.GetRectVectors(vectors.GetRange(vectors.Count - 3, 3));
                if(newVectors.Count != 2)
                    return 0;
                rectVectors.AddRange(newVectors);
            }
            if (rectVectors.Count != 8)
                return 0;

            var cornersOrder = new List<int> {0};
            if (vectors[0].Length < vectors[1].Length)
            {
                cornersOrder.Add(1);
                if (vectors[1].Length < vectors[2].Length)
                {
                    cornersOrder.Add(3);
                    cornersOrder.Add(2);
                }
                else
                {
                    cornersOrder.Add(2);
                    cornersOrder.Add(3);
                }
            }
            else
            {
                cornersOrder.Add(2);
                if (vectors[0].Length > vectors[2].Length)
                {
                    cornersOrder.Add(1);
                    cornersOrder.Add(3);
                }
                else
                {
                    cornersOrder.Add(3);
                    cornersOrder.Add(1);
                }
            }
            for (var i = 1; i < cornersOrder.Count; i++ )
            {
                if (DrawLine(corners[cornersOrder[i - 1]], corners[cornersOrder[i]])) continue;
                return 0;
            }
            if (!DrawLine(corners[3], corners[0]))
                return 0;
            if (collection.Count != visitedNodes.Count)
                return 0;


            var basedVectors = rectVectors.Skip(0).Take(2).ToList();
            CheckPure(basedVectors);
            return basedVectors[0].Length * basedVectors[1].Length;
        }

        /// <summary>
        /// определяет с дыркой прямогульник или нет
        /// </summary>
        /// <param name="vectors"></param>
        private void CheckPure(IList<Vector> vectors)
        {
            if (vectors[0].Length < 2 || vectors[1].Length < 2)
                IsPure = false;
            else
                IsPure = true;
        }
        /// <summary>
        /// находятся ли 2 точки на вертикальной линии
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        private static bool IsVertical(Node one, Node two)
        {
            return one.I == two.I;
        }
        /// <summary>
        /// находятся ли 2 точки на горизонтальной линии
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        private static bool IsHorizontal(Node one, Node two)
        {
            return one.J == two.J;
        }
        /// <summary>
        /// проверяет можно ли нарисовать линию между 2 точками
        /// есть ли все точки необходимые для построения линии
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        private bool DrawLine(Node one, Node two)
        {
            var value = Map.GetValueNode(one);
            var lower = one.J < two.J ? one : two;
            var top = one.J < two.J ? two : one;
            if (IsVertical(one, two))
            {
                for (int j = lower.J; j <= top.J; ++j)
                {
                    if (!Map.FindWithoutColor(one.I, j, value))
                        return false;
                    var node = new Node(one.I, j);
                    if (!visitedNodes.Contains(node))
                        visitedNodes.Add(node);
                }
                return true;
            }
            lower = one.I < two.I ? one : two;
            top = one.I < two.I ? two : one;
            if (IsHorizontal(one, two))
            {
                for (int i = lower.I; i <= top.I; ++i)
                {
                    if (!Map.FindWithoutColor(i, one.J, value))
                        return false;
                    var node = new Node(i, one.J);
                    if (!visitedNodes.Contains(node))
                        visitedNodes.Add(node);
                }
                return true;
            }
            var line = new Line(one, two);
            // тут надо поменять поиск точек
            if (Math.Abs(one.I - two.I) >= Math.Abs(one.J - two.J))
                for (int i = lower.I; i <= top.I; ++i)
                {
                    if (!Map.FindWithoutColor(i, line.Y(i), value))
                        return false;
                    var node = new Node(i, line.Y(i));
                    if (!visitedNodes.Contains(node))
                        visitedNodes.Add(node);
                }
            else
            {
                lower = one.J < two.J ? one : two;
                top = one.J < two.J ? two : one;
                for (int j = lower.J; j <= top.J; ++j)
                {
                    if (!Map.FindWithoutColor(line.X(j), j, value))
                        return false;
                    var node = new Node(line.X(j), j);
                    if (!visitedNodes.Contains(node))
                        visitedNodes.Add(node);
                }
            }
            return true;
        }
    }
    /// <summary>
    /// линия
    /// нужна для построения значения у(х) или х(у)
    /// </summary>
    class Line
    {
        Node One{ get; set;}
        Node Two{ get; set;}
        public Line(Node one, Node two)
        {
            One = one;
            Two = two;
        }
        public int Y(int x)
        {
            return (Two.J - One.J)*(x - One.I)/(Two.I - One.I) + One.J;
        }
        public int X(int y)
        {
            return (Two.I - One.I)*(y - One.I)/(Two.J - One.J) + One.I;
        }
    }
}
